package ksv.manager;

import lombok.Getter;
import lombok.Setter;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.aop.framework.ProxyConfig;

import javax.net.SocketFactory;
import javax.net.ssl.*;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

/**
 * @创建人 wsdc
 * @时间 2021/2/28
 * @描述 $
 */
public class OkHttpSocks5 {



    public static final X509TrustManager NOP_TRUST_MANAGER = new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
                throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
                throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    };

    public static final HostnameVerifier NOP_HOSTNAME_VERIFIER = new HostnameVerifier() {
        @Override
        public boolean verify(String s, SSLSession sslSession) {
            return true;
        }
    };

    static SSLContext NOP_TLSV12_SSL_CONTEXT;
    static {
        try {
            NOP_TLSV12_SSL_CONTEXT= SSLContext.getInstance("TLSv1.2");
            NOP_TLSV12_SSL_CONTEXT.init(null, new TrustManager[]{NOP_TRUST_MANAGER}, new java.security.SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
    }

    public static class ProxySocketFactory extends SocketFactory {

        ProxyConfig proxyConfig;

        public ProxySocketFactory(ProxyConfig proxyConfig) {
            this.proxyConfig = proxyConfig;
        }

        @Override
        public Socket createSocket() throws IOException {
            if (proxyConfig != null) {
                return new Socket(proxyConfig.getProxy());
            } else {
                return new Socket();
            }
        }

        public Socket createSocket(String host, int port)
                throws IOException, UnknownHostException {
            Socket socket = createSocket();
            try {
                socket.connect(new InetSocketAddress(host, port));
            } catch (IOException e) {
                socket.close();
                throw e;
            }
            return socket;
        }

        public Socket createSocket(InetAddress address, int port)
                throws IOException {
            Socket socket = createSocket();
            try {
                socket.connect(new InetSocketAddress(address, port));
            } catch (IOException e) {
                socket.close();
                throw e;
            }
            return socket;
        }

        public Socket createSocket(String host, int port,
                                   InetAddress clientAddress, int clientPort)
                throws IOException, UnknownHostException {
            Socket socket = createSocket();
            try {
                socket.bind(new InetSocketAddress(clientAddress, clientPort));
                socket.connect(new InetSocketAddress(host, port));
            } catch (IOException e) {
                socket.close();
                throw e;
            }
            return socket;
        }

        public Socket createSocket(InetAddress address, int port,
                                   InetAddress clientAddress, int clientPort)
                throws IOException {
            Socket socket = createSocket();
            try {
                socket.bind(new InetSocketAddress(clientAddress, clientPort));
                socket.connect(new InetSocketAddress(address, port));
            } catch (IOException e) {
                socket.close();
                throw e;
            }
            return socket;
        }
    }

    @Setter
    @Getter
    public static class ProxyConfig {
        private Proxy proxy;
        private PasswordAuthentication authentication;
    }

    public static class ProxySSLSocketFactory extends SSLSocketFactory {
        private ProxyConfig config;
        private SSLSocketFactory socketFactory;

        public ProxySSLSocketFactory(ProxyConfig config, SSLSocketFactory socketFactory) {
            this.config = config;
            this.socketFactory = socketFactory;
        }

        @Override
        public String[] getDefaultCipherSuites() {
            return socketFactory.getDefaultCipherSuites();
        }

        @Override
        public String[] getSupportedCipherSuites() {
            return socketFactory.getSupportedCipherSuites();
        }

        public Socket createSocket()
                throws IOException {
            ProxyConfig proxyConfig = config;
            if (proxyConfig != null) {
                return new Socket(proxyConfig.getProxy());
            } else {
                return new Socket();
            }
        }

        public Socket createSocket(String host, int port)
                throws IOException {
            Socket socket = createSocket();
            try {
                return socketFactory.createSocket(socket, host, port, true);
            } catch (IOException e) {
                socket.close();
                throw e;
            }
        }

        public Socket createSocket(Socket s, String host,
                                   int port, boolean autoClose)
                throws IOException {
            //TODO 无法代理
            return socketFactory.createSocket(s, host, port, autoClose);
        }

        public Socket createSocket(InetAddress address, int port)
                throws IOException {
            Socket socket = createSocket();
            try {
                return socketFactory.createSocket(socket, address.getHostAddress(), port, true);
            } catch (IOException e) {
                socket.close();
                throw e;
            }
        }

        public Socket createSocket(String host, int port,
                                   InetAddress clientAddress, int clientPort)
                throws IOException {
            Socket socket = createSocket();
            try {
                socket.bind(new InetSocketAddress(clientAddress, clientPort));
                return socketFactory.createSocket(socket, host, port, true);
            } catch (IOException e) {
                socket.close();
                throw e;
            }
        }

        public Socket createSocket(InetAddress address, int port,
                                   InetAddress clientAddress, int clientPort)
                throws IOException {
            Socket socket = createSocket();
            try {
                socket.bind(new InetSocketAddress(clientAddress, clientPort));
                return socketFactory.createSocket(socket, address.getHostAddress(), port, true);
            } catch (IOException e) {
                socket.close();
                throw e;
            }
        }
    }

    public static OkHttpClient proxyOkHttpClient(String host,int port,String username,String password){
        ProxyConfig config = new ProxyConfig();
        Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(host, port));
        config.setProxy(proxy);
        OkHttpClient httpClient = new OkHttpClient.Builder()
                .sslSocketFactory(new ProxySSLSocketFactory(config, NOP_TLSV12_SSL_CONTEXT.getSocketFactory()), NOP_TRUST_MANAGER)
                .socketFactory(new ProxySocketFactory(config))
                .hostnameVerifier(NOP_HOSTNAME_VERIFIER)
                .build();
        return httpClient;
    }

    public static void main(String[] args) {
        OkHttpClient client = proxyOkHttpClient("101.32.185.243", 1084, "", "");

        Request request = new Request.Builder()
                .url("https://developer.android.google.cn/guide/components/activities/intro-activities")
                .build();

        try {
            Response response = client.newCall(request).execute();
            int code = response.code();
            String string = response.body().string();
            int k = 0;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
